using System;
using FireStarter.Base;
using FireStarter.Base.BusinessObjectMerger;
using Firestarter.DB.Schema.BO;
using Firestarter.DB.Schema.Logic;
using Firestarter.DB.Schema.Test.BO;
using Firestarter.DB.Schema.Test.BO.TestUtils;
using Firestarter.DB.Schema.Test.Logic.DBMerge;
using Habanero.BO;
using Habanero.BO.ClassDefinition;
using Habanero.BO.Loaders;
using Habanero.Testability;
using NUnit.Framework;

namespace Firestarter.DB.Schema.Test.Logic
{
    [TestFixture]
    public class TestMergeDBForeignKeyColumn
    {
        [TestFixtureSetUp]
        public virtual void TestFixtureSetup()
        {
            ClassDef.ClassDefs.Clear();
            string xmlClassDefs = BOBroker.GetClassDefsXml();
            ClassDef.ClassDefs.Add(new XmlClassDefsLoader(xmlClassDefs, new DtdLoader()).LoadClassDefs());
            BORegistry.DataAccessor = new DataAccessorInMemory();

            MergeableBORegistry.Instance.Register<DBForeignKeyColumn, DBMergeDBForeignKeyColumn>();
        }

        [SetUp]
        public virtual void SetupTest()
        {
        }

        [TestCase("ForeignKeyColumnID", false)]
        [TestCase("ForeignKeyID", false)]
        [TestCase("ParentColumnID", false)]
        [TestCase("RelatedColumnID", false)]
        [TestCase("Approved", false)]
        [TestCase("PendingChangeType", false)]
        public void Test_MustMergeProp_ShouldBeSetForProp(string propName, bool expectedMustMergeProp)
        {
            //---------------Set up test pack-------------------
            DBMergeBO<DBForeignKeyColumn> merger = new DBMergeBO<DBForeignKeyColumn>();
            var dbForeignKey = new DBForeignKeyColumn();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            var boProp = dbForeignKey.Props[propName];
            var returnedMustMergeProp = merger.MustMergeProperty(boProp);
            //---------------Test Result -----------------------
            Assert.AreEqual(expectedMustMergeProp, returnedMustMergeProp, "Prop '" + propName + "' should be MustMerge = '" + expectedMustMergeProp +"'");
        }

        [TestCase("ParentColumn", false)]
        [TestCase("ParentColumn", false)]
        [TestCase("ForeignKey", false)]
        public void Test_MustMergeRelationship_ShouleBeSetForRelationships(string relationshipName, bool expectedMustMergeRel)
        {
            //---------------Set up test pack-------------------
            DBMergeBO<DBForeignKeyColumn> merger = new DBMergeBO<DBForeignKeyColumn>();
            var dbForeignKey = new DBForeignKeyColumn();
            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------

            var relationship = dbForeignKey.Relationships[relationshipName];
            var returnedMustMergeProp = merger.MustMergeRelationship(relationship);
            //---------------Test Result -----------------------
            Assert.AreEqual(expectedMustMergeRel, returnedMustMergeProp, "Relationship '" + relationship.RelationshipName + "' should be MustMerge = '" + expectedMustMergeRel + "'");
        }

        [Test]
        public void Test_MatchFKColumn_WhenFKNameAndOwnerPropSame_ShouldMatch()
        {
            //---------------Set up test pack-------------------
            DBMergeBO<DBForeignKeyColumn> fkColumnMerger = (DBMergeBO<DBForeignKeyColumn>)MergeableBORegistry.Instance.Resolve<DBForeignKeyColumn>();
            var fromFKColumn = CreateFKColumnWithFK(RandomValueGen.GetRandomString(), RandomValueGen.GetRandomString());


            var toFKColumn = CreateFKColumnWithFK(fromFKColumn.ForeignKey.ForeignKeyName, fromFKColumn.ParentColumnName, fromFKColumn.RelatedColumnName);
            //---------------Assert Precondition----------------
            Assert.IsNotNull(toFKColumn.ParentColumn);
            Assert.IsNotNull(fromFKColumn.ParentColumn);
            Assert.AreEqual(toFKColumn.ParentColumnName, fromFKColumn.ParentColumnName);
            Assert.AreEqual(toFKColumn.ForeignKey.ForeignKeyName, fromFKColumn.ForeignKey.ForeignKeyName);
            Assert.AreEqual(toFKColumn.ToString(), fromFKColumn.ToString());
            //---------------Execute Test ----------------------
            //fkColumnMerger.BusinessObject = fromFKColumn;
            Func<DBForeignKeyColumn, bool> matchFunction = fkColumnMerger.MatchBusinessObject(fromFKColumn);
            var matchBusinessObject = matchFunction(toFKColumn);
            //---------------Test Result -----------------------
            Assert.IsTrue(matchBusinessObject, "FK Columns should match");
        }
        [Test]
        public void Test_MatchFKColumn_WhenFKNameAndOwnerPropSame_ButKeyNameNotSame_ShouldMatch()
        {
            //---------------Set up test pack-------------------
            DBMergeBO<DBForeignKeyColumn> fkColumnMerger = (DBMergeBO<DBForeignKeyColumn>)MergeableBORegistry.Instance.Resolve<DBForeignKeyColumn>();
            var fromFKColumn = CreateFKColumnWithFK(RandomValueGen.GetRandomString(), RandomValueGen.GetRandomString());


            var toFKColumn = CreateFKColumnWithFK(GetRandomString(), fromFKColumn.ParentColumnName, fromFKColumn.RelatedColumnName);
            //---------------Assert Precondition----------------
            Assert.IsNotNull(toFKColumn.ParentColumn);
            Assert.IsNotNull(fromFKColumn.ParentColumn);
            Assert.AreEqual(toFKColumn.ParentColumnName, fromFKColumn.ParentColumnName);
            Assert.AreEqual(toFKColumn.RelatedColumnName, fromFKColumn.RelatedColumnName);
            Assert.AreNotEqual(toFKColumn.ForeignKey.ForeignKeyName, fromFKColumn.ForeignKey.ForeignKeyName);
            Assert.AreNotEqual(toFKColumn.ToString(), fromFKColumn.ToString());
            //---------------Execute Test ----------------------
            //fkColumnMerger.BusinessObject = fromFKColumn;
            Func<DBForeignKeyColumn, bool> matchFunction = fkColumnMerger.MatchBusinessObject(fromFKColumn);
            var matchBusinessObject = matchFunction(toFKColumn);
            //---------------Test Result -----------------------
            Assert.IsTrue(matchBusinessObject, "FK Columns should match");
        }

        [Test]
        public void Test_MatchFKColumn_WhenFKNameNotMatchAndOwnerPropSame_ShouldNotMatch()
        {
            //---------------Set up test pack-------------------
            DBMergeBO<DBForeignKeyColumn> fkColumnMerger = (DBMergeBO<DBForeignKeyColumn>)MergeableBORegistry.Instance.Resolve<DBForeignKeyColumn>();
            var fromFKColumn = CreateFKColumnWithFK(RandomValueGen.GetRandomString(), RandomValueGen.GetRandomString());
            var toFKColumn = CreateFKColumnWithFK(fromFKColumn.ForeignKey.ForeignKeyName, RandomValueGen.GetRandomString());
            //---------------Assert Precondition----------------
            Assert.AreNotEqual(toFKColumn.ParentColumnName, fromFKColumn.ParentColumnName);
            Assert.AreEqual(toFKColumn.ForeignKey.ForeignKeyName, fromFKColumn.ForeignKey.ForeignKeyName);
            Assert.AreNotEqual(toFKColumn.ToString(), fromFKColumn.ToString());
            //---------------Execute Test ----------------------
            var function = fkColumnMerger.MatchBusinessObject(fromFKColumn);
            var matchBusinessObject = function(toFKColumn);
            //---------------Test Result -----------------------
            Assert.IsFalse(matchBusinessObject, "FK Columns should not match");
        }

        [Test]
        public void Test_MatchFKColumn_WhenFKNametMatchAndOwnerPropMatch_ButRelatedPropNoMatch_ShouldNotMatch()
        {
            //---------------Set up test pack-------------------
            DBMergeBO<DBForeignKeyColumn> fkColumnMerger = (DBMergeBO<DBForeignKeyColumn>)MergeableBORegistry.Instance.Resolve<DBForeignKeyColumn>();
            var fromFKColumn = CreateFKColumnWithFK(RandomValueGen.GetRandomString(), RandomValueGen.GetRandomString());
            var toFKColumn = CreateFKColumnWithFK(fromFKColumn.ForeignKey.ForeignKeyName,fromFKColumn.ParentColumnName, RandomValueGen.GetRandomString());
            //---------------Assert Precondition----------------
            Assert.AreEqual(toFKColumn.ParentColumnName, fromFKColumn.ParentColumnName);
            Assert.AreNotEqual(toFKColumn.RelatedColumnName, fromFKColumn.RelatedColumnName);
            Assert.AreEqual(toFKColumn.ForeignKey.ForeignKeyName, fromFKColumn.ForeignKey.ForeignKeyName);
            Assert.AreNotEqual(toFKColumn.ToString(), fromFKColumn.ToString());
            //---------------Execute Test ----------------------
            var function = fkColumnMerger.MatchBusinessObject(fromFKColumn);
            var matchBusinessObject = function(toFKColumn);
            //---------------Test Result -----------------------
            Assert.IsFalse(matchBusinessObject, "FK Columns should not match");
        }

        [Test]
        public void Test_MergeFKColumn_WhenParentColumnNull_AndRelatedColNull_ShouldDoNothing()
        {
            //---------------Set up test pack-------------------
            DBMergeBO<DBForeignKeyColumn> keyColumnMerger =
                (DBMergeBO<DBForeignKeyColumn>)MergeableBORegistry.Instance.Resolve<DBForeignKeyColumn>();

            var fromFKColumn = CreateFKColumnWithFK();
            fromFKColumn.ParentColumn = null;
            fromFKColumn.RelatedColumn = null;
            var toFKColumn = CreateFKColumnWithFK();
            //---------------Assert Precondition----------------
            Assert.IsNull(fromFKColumn.ParentColumn);
            Assert.IsNull(fromFKColumn.RelatedColumn);
            Assert.IsNotNull(fromFKColumn.ForeignKey);
            var fromTable = fromFKColumn.ForeignKey.ParentTable;
            Assert.IsNotNull(fromTable);

            Assert.IsNotNull(toFKColumn.ParentColumn);
            Assert.IsNotNull(toFKColumn.RelatedColumn);
            Assert.IsNotNull(toFKColumn.ForeignKey);
            var toTable = toFKColumn.ForeignKey.ParentTable;
            Assert.IsNotNull(toTable);
            //---------------Execute Test ----------------------
            keyColumnMerger.MergeBOFrom(fromFKColumn, toFKColumn);
            //---------------Test Result -----------------------
            Assert.IsNotNull(toFKColumn.ParentColumn);
            Assert.IsNotNull(toFKColumn.RelatedColumn);
            Assert.IsNotNull(toFKColumn.ForeignKey);
        }

        [Test]
        public void Test_MergeFKColumn_WhenParentColumnNotNull_AndRelatedColNull_ShouldMapParentColumn()
        {
            //---------------Set up test pack-------------------
            DBMergeBO<DBForeignKeyColumn> keyColumnMerger =
                (DBMergeBO<DBForeignKeyColumn>)MergeableBORegistry.Instance.Resolve<DBForeignKeyColumn>();

            var fromFKColumn = CreateFKColumnWithFK(GetRandomString(), GetRandomString());
            fromFKColumn.RelatedColumn = null;
            var toFKColumn = CreateFKColumnWithFK(fromFKColumn.ForeignKey.ForeignKeyName, fromFKColumn.ParentColumnName);
            toFKColumn.ParentColumn = null;
            //---------------Assert Precondition----------------
            Assert.IsNotNull(fromFKColumn.ParentColumn);
            Assert.IsNull(fromFKColumn.RelatedColumn);
            Assert.IsNotNull(fromFKColumn.ForeignKey);
            var fromTable = fromFKColumn.ForeignKey.ParentTable;
            Assert.IsNotNull(fromTable);

            Assert.IsNull(toFKColumn.ParentColumn);
            Assert.IsNotNull(toFKColumn.RelatedColumn);
            Assert.IsNotNull(toFKColumn.ForeignKey);
            var toTable = toFKColumn.ForeignKey.ParentTable;
            Assert.IsNotNull(toTable);
            var expectedToParentColumn = toTable.Columns.Find(column => column.ColumnName == fromFKColumn.ParentColumnName);
            Assert.IsNotNull(expectedToParentColumn);

            //---------------Execute Test ----------------------
            keyColumnMerger.MergeBOFrom(fromFKColumn, toFKColumn);
            //---------------Test Result -----------------------
            Assert.IsNotNull(toFKColumn.ParentColumn);
            Assert.AreSame(expectedToParentColumn, toFKColumn.ParentColumn);
            Assert.IsNotNull(toFKColumn.RelatedColumn);
            Assert.IsNotNull(toFKColumn.ForeignKey);
        }

        [Test]
        public void Test_MergeFKColumn_WhenParentColumnNull_AndRelatedColNotNull_ShouldMapParentColumn()
        {
            //---------------Set up test pack-------------------
            DBMergeBO<DBForeignKeyColumn> keyColumnMerger =
                (DBMergeBO<DBForeignKeyColumn>)MergeableBORegistry.Instance.Resolve<DBForeignKeyColumn>();

            var fromFKColumn = CreateFKColumnWithFK();
            fromFKColumn.ParentColumn = null;
            var toFKColumn = CreateFKColumnWithFK(fromFKColumn.ForeignKey.ForeignKeyName, fromFKColumn.ParentColumnName, fromFKColumn.RelatedColumnName);
            toFKColumn.RelatedColumn = null;
            //---------------Assert Precondition----------------
            Assert.IsNull(fromFKColumn.ParentColumn);
            Assert.IsNotNull(fromFKColumn.RelatedColumn);
            Assert.IsNotNull(fromFKColumn.ForeignKey);
            var fromTable = fromFKColumn.ForeignKey.ParentTable;
            Assert.IsNotNull(fromTable);

            Assert.IsNotNull(toFKColumn.ParentColumn);
            Assert.IsNull(toFKColumn.RelatedColumn);
            Assert.IsNotNull(toFKColumn.ForeignKey);
            var toRelatedTable = toFKColumn.ForeignKey.RelatedTable;
            Assert.IsNotNull(toRelatedTable);
            var expectedRelatedColumn = toRelatedTable.Columns.Find(column => column.ColumnName == fromFKColumn.RelatedColumnName);
            Assert.IsNotNull(expectedRelatedColumn);

            //---------------Execute Test ----------------------
            keyColumnMerger.MergeBOFrom(fromFKColumn, toFKColumn);
            //---------------Test Result -----------------------
            Assert.IsNotNull(toFKColumn.ParentColumn);
            Assert.IsNotNull(toFKColumn.RelatedColumn);
            Assert.AreSame(expectedRelatedColumn, toFKColumn.RelatedColumn);
            Assert.IsNotNull(toFKColumn.ForeignKey);
        }

        [Test]
        public void Test_MergeFKColumn_WhenPropNameSame_WhenToTableDoesNotHaveProp_ShouldSetColumnRelationshipOnFKColumn()
        {
            //---------------Set up test pack-------------------
            DBMergeBO<DBForeignKeyColumn> keyColumnMerger =
                (DBMergeBO<DBForeignKeyColumn>)MergeableBORegistry.Instance.Resolve<DBForeignKeyColumn>();

            var fromFKColumn = CreateFKColumnWithFK();

            var toFKColumn = CreateFKColumnWithFK(fromFKColumn.ForeignKey.ForeignKeyName, fromFKColumn.ParentColumnName, fromFKColumn.RelatedColumnName);
            toFKColumn.ParentColumn = null;
            //---------------Assert Precondition----------------
            Assert.IsNotNull(fromFKColumn.ParentColumn);
            Assert.IsNotNull(fromFKColumn.ForeignKey);
            var fromParentTable = fromFKColumn.ForeignKey.ParentTable;
            Assert.IsNotNull(fromParentTable);
            Assert.IsTrue(fromParentTable.Columns.Contains(fromFKColumn.ParentColumn), "From Parent Table should contain FK.ParentColumn");

            Assert.IsNull(toFKColumn.ParentColumn);
            Assert.IsNotNull(toFKColumn.ForeignKey);
            var toTable = toFKColumn.ForeignKey.ParentTable;
            Assert.IsNotNull(toTable);
            var expectedToColumn = toTable.Columns.Find(column => column.ColumnName == fromFKColumn.ParentColumn.ColumnName);
            Assert.IsNotNull(expectedToColumn);
            //---------------Execute Test ----------------------
            keyColumnMerger.MergeBOFrom(fromFKColumn, toFKColumn);
            //---------------Test Result -----------------------
            Assert.AreSame(expectedToColumn, toFKColumn.ParentColumn);
        }

        private static string GetRandomString()
        {
            return RandomValueGen.GetRandomString();
        }

        private static DBForeignKeyColumn CreateFKColumnWithFK(string fkName, string parentColumnName, string relatedColumnName)
        {
            var foreignKey = TestUtilsDBForeignKey.GetTestFactory()
                .WithValue(relationship => relationship.ForeignKeyName, fkName)
                .CreateValidBusinessObject();

            if(foreignKey.RelatedTable == null)
            {
                foreignKey.RelatedTable = TestUtilsDBTable.GetTestFactory().CreateValidBusinessObject();
            }

            var parentColumn = TestUtilsDBColumn.GetTestFactory()
                .WithValue(column => column.ColumnName, parentColumnName)
                .WithValue(column => column.Table, foreignKey.ParentTable)
                .CreateValidBusinessObject();


            DBColumn relatedColumn = null;
            if (!string.IsNullOrEmpty(relatedColumnName))
            {
                relatedColumn = TestUtilsDBColumn.GetTestFactory()
                    .WithValue(column => column.ColumnName, relatedColumnName)
                    .WithValue(column => column.Table, foreignKey.RelatedTable)
                    .CreateValidBusinessObject();
            }

            var fkColumn = TestUtilsDBForeignKeyColumn.GetTestFactory()
                .WithValue(property => property.ForeignKey, foreignKey)
                .WithValue(property => property.RelatedColumn, relatedColumn)
                .WithValue(property => property.ParentColumn, parentColumn)
                .CreateValidBusinessObject();
            return fkColumn;
        }


        private static DBForeignKeyColumn CreateFKColumnWithFK()
        {
            return CreateFKColumnWithFK(GetRandomString(), GetRandomString(), GetRandomString());
        }
        private static DBForeignKeyColumn CreateFKColumnWithFK(string fkName)
        {
            return CreateFKColumnWithFK(fkName, GetRandomString(), GetRandomString());
        }

        private static DBForeignKeyColumn CreateFKColumnWithFK(string fkName, string parentColumnName)
        {
            return CreateFKColumnWithFK(fkName, parentColumnName, RandomValueGen.GetRandomString());
        }
    }
}